﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

namespace OnlineSupermarket
{
    public sealed partial class OnlineSupermarket
    {
        /// <summary>
        /// 表示一辆购物车。
        /// </summary>
        public sealed class Cart : ICollection<GoodsInfo>
        {
            private readonly List<GoodsInfo> goods;

            public Cart()
            {
                this.goods = new List<GoodsInfo>();
            }

            public int Count => this.goods.Count;
            bool ICollection<GoodsInfo>.IsReadOnly => false;

            public void Add(GoodsInfo goods)
            {
                if (goods == null)
                    throw new ArgumentNullException(nameof(goods));
                this.goods.Add(goods);
            }

            public bool Remove(GoodsInfo goods)
            {
                if (goods == null)
                    throw new ArgumentNullException(nameof(goods));
                return this.goods.Remove(goods);
            }

            public void Clear()
            {
                this.goods.Clear();
            }

            public bool Contains(GoodsInfo? goods)
            {
                if (goods == null)
                    return false;
                return this.goods.Contains(goods);
            }

            /// <summary>
            /// 结账取得收据。
            /// 若要正确结账，购物车里的所有物品都在线上超市中存在，否则返回 <c>null</c> 。
            /// </summary>
            /// <param name="supermarkets">用以判断物品是否存在的线上超市。</param>
            /// <returns>收据。</returns>
            public Receipt? CheckOutIn(params OnlineSupermarket[] supermarkets)
            {
                if (supermarkets == null)
                    return null;

                var allGoods = (from supermarket in supermarkets
                                from goods in supermarket
                                select goods).ToHashSet();

                Dictionary<GoodsInfo, int> result = new Dictionary<GoodsInfo, int>();
                foreach (var good in this.goods)
                {
                    if (result.TryGetValue(good, out int weight))
                        result[good] = weight + 1;
                    else if (!allGoods.Contains(good))
                        return null;
                    else
                        result.Add(good, 1);
                }

                return new Receipt(result);
            }

            void ICollection<GoodsInfo>.CopyTo(GoodsInfo[] array, int arrayIndex)
            {
                this.goods.CopyTo(array, arrayIndex);
            }

            IEnumerator IEnumerable.GetEnumerator()
            {
                return this.goods.GetEnumerator();
            }

            IEnumerator<GoodsInfo> IEnumerable<GoodsInfo>.GetEnumerator()
            {
                return this.goods.GetEnumerator();
            }
        }
    }
}